package com.gee.spring.cloud.data.transform.core.dispatch;

import cn.hutool.core.util.HexUtil;
import com.gee.spring.cloud.common.core.domain.center.config.ChannelHandlerConfigDto;
import com.gee.spring.cloud.common.core.domain.center.config.SocketConfigDto;
import com.gee.spring.cloud.common.core.domain.frame.CstpWrapper;
import com.gee.spring.cloud.common.core.domain.socket.IChannelHandlerConf;
import com.gee.spring.cloud.common.core.domain.socket.SocketType;
import com.gee.spring.cloud.common.core.entity.center.config.TransformDsl;
import com.gee.spring.cloud.common.core.enums.SendReceive;
import com.gee.spring.cloud.common.core.enums.YesNo;
import com.gee.spring.cloud.common.starter.socket.Socket;
import com.gee.spring.cloud.common.starter.socket.dispatcher.DataDispatcher;
import com.gee.spring.cloud.data.transform.core.center.config.handler.TransformDslHandler;
import com.gee.spring.cloud.data.transform.core.util.DslUtil;
import io.netty.channel.Channel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.net.InetSocketAddress;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;

import static com.gee.spring.cloud.data.transform.core.constant.TransformConstant.DLS_TO_CSTP_HEAD_METHOD_NAME;
import static com.gee.spring.cloud.data.transform.core.constant.TransformConstant.DLS_TO_INNER_METHOD_NAME;

/**
 * @author gepengjun
 * @since 2024/6/18 15:40
 */
@Component
@Slf4j
public class CustomerDataDispatcher implements DataDispatcher {

    @Autowired
   private BaseDataDispatcher baseDataDispatcher;

    @Autowired
    private TransformDslHandler transformDslHandler;

    /**
     * 处理客户数据
     * @param bytes 客户数据
     * @param socket 来源socket
     * @param fromChannel 来源channel
     */
    @Override
    public void dispatch(byte[] bytes, Socket socket, Channel fromChannel) {
        CstpWrapper cstpHead = toCstpWrapper(bytes, DLS_TO_CSTP_HEAD_METHOD_NAME, baseDataDispatcher.getCstpHeadDsl());
        Optional<ChannelHandlerConfigDto> channelHandlerConfigDtoOptional = getChannelHandlerConfigDto(cstpHead, socket, fromChannel);
        if (channelHandlerConfigDtoOptional.isPresent()){
            ChannelHandlerConfigDto channelHandlerConfigDto = channelHandlerConfigDtoOptional.get();
            CstpWrapper cstpWrapper = toCstpWrapper(bytes, DLS_TO_INNER_METHOD_NAME, channelHandlerConfigDto.getTransformDsl());
            log.info("转换为本中心数据: {}", cstpWrapper.toHex());
        }else {
            log.warn("未配置信道处理器, 客户数据: {}, cstpHead: {}", HexUtil.encodeHexStr(bytes).toUpperCase(), cstpHead);
        }
    }

    private Optional<ChannelHandlerConfigDto> getChannelHandlerConfigDto(CstpWrapper cstpHead, Socket socket, Channel fromChannel) {
        SocketConfigDto socketConfigDto = socket.getSocketConfigDto();
        if (socketConfigDto.getSocketType().equals(SocketType.TCP_SERVER.name())){
            InetSocketAddress socketAddress = (InetSocketAddress)fromChannel.remoteAddress();
            return socketConfigDto.getClientConfigDtoMap().values().stream()
                    .filter(socketClientConfigDto -> socketClientConfigDto.getRemoteIp().equals(socketAddress.getAddress().getHostAddress())
                            && socketClientConfigDto.getRemotePort() == socketAddress.getPort())
                    .map(socketClientConfigDto ->
                            filterChannelHandlerConfig(cstpHead, socketClientConfigDto.getChannelHandlerConfMap())
                    )
                    .flatMap(Collection::stream)
                    .findFirst();
        }else {
            return filterChannelHandlerConfig(cstpHead, socketConfigDto.getChannelHandlerConfMap()).stream().findFirst();
        }
    }

    private List<ChannelHandlerConfigDto> filterChannelHandlerConfig(CstpWrapper cstpHead, Map<Long, IChannelHandlerConf> channelHandlerConfMap){
        return channelHandlerConfMap.values().stream()
                .map(iChannelHandlerConf -> (ChannelHandlerConfigDto) iChannelHandlerConf)
                .filter(channelHandlerConfigDto -> channelHandlerConfigDto.getEnable() == YesNo.YES.getCode()
                        && channelHandlerConfigDto.getSendReceive().equals(SendReceive.RECEIVE.name()))
                .filter(channelHandlerConfigDto -> {
                    boolean filterSate;
                    boolean filterDevice;
                    if (channelHandlerConfigDto.getEnableChannelFilterSate() == YesNo.YES.getCode()) {
                        filterSate = channelHandlerConfigDto.getFilterSateList().stream()
                                .anyMatch(satellite -> satellite.getSateIdentity() == cstpHead.getTid());
                    }else {
                        filterSate = baseDataDispatcher.filterSatellite(cstpHead.getTid());
                    }

                    if (channelHandlerConfigDto.getEnableChannelFilterDevice() == YesNo.YES.getCode()) {
                        filterDevice = channelHandlerConfigDto.getFilterDeviceList().stream()
                                .anyMatch(device -> device.getDevIdentity() == cstpHead.getDid());
                    }else {
                        filterDevice = baseDataDispatcher.filterDevice(cstpHead.getDid());
                    }

                    boolean filterFrame = channelHandlerConfigDto.getFilterFrameList().stream()
                            .anyMatch(frame -> frame.getBid() == cstpHead.getBid());
                    return filterSate && filterDevice && filterFrame;
                })
                .collect(Collectors.toList());
    }

    private CstpWrapper toCstpWrapper(byte[] bytes, String methodName, TransformDsl transformDsl){
        try {
            Object result = DslUtil.invokeStatic(methodName, transformDsl.getDslScript(),
                     baseDataDispatcher.getOrg(), baseDataDispatcher.getOrgCenter(), bytes);
            return (CstpWrapper) result;
        } catch (Exception e) {
            String format = String.format("执行转换为Cstp脚本出错, 脚本名称: %s, 数据: %s", transformDsl.getDslName(),
                    HexUtil.encodeHexStr(bytes));
            throw new RuntimeException(format, e);
        }
    }
}
